﻿using System;
using System.Collections.Generic;
using System.Text;

using System.Web;
using System.Configuration;
using System.IO;

namespace InSolve.dmach.FileDeny
{
    public class FileDenyModule : IHttpModule
    {
        const string KEY_GET_PREFIX = "key=";
        const string KEY_FILE = "keyFile";
        const string EXT_MAPPING = "extMapping";
        static char[] EXT_SEPARATORS = { ';', ',', ':', '|' };
        const string KEY_DATE_FORMAT = "dateFormat";
        const StringComparison CMP_OPTION = StringComparison.CurrentCultureIgnoreCase;


        #region IHttpModule Members

        public void Dispose()
        {
            //throw new NotImplementedException();
        }

        public void Init(HttpApplication context)
        {
            context.BeginRequest += (sender,e) =>
                {
                    HttpApplication app = sender as HttpApplication;
                    if (app == null)
                        return;

                    //
                    // Get configuration parameters
                    //

                    string dbFile = ConfigurationManager.AppSettings.Get(KEY_FILE);
                    string mapping = ConfigurationManager.AppSettings.Get(EXT_MAPPING);
                    string dateFormat = ConfigurationManager.AppSettings.Get(KEY_DATE_FORMAT);
                    if (dbFile == null || mapping == null)
                        return;

                    string[] extList = mapping.Split(EXT_SEPARATORS, StringSplitOptions.RemoveEmptyEntries);
                    dbFile = app.Context.Request.MapPath(dbFile);
                    if (extList.Length == 0)
                        return;
                    if (!File.Exists(dbFile))
                        return;

                    //
                    // Mapping extensions
                    //

                    bool mappingApply = false;
                    foreach (string ext in extList)
                    {
                        if (app.Request.Url.AbsolutePath.EndsWith(ext, CMP_OPTION))
                        {
                            mappingApply = true;
                            break;
                        }
                    }

                    if (!mappingApply)
                        return;

                    //
                    // Get key from request
                    //

                    string reqKey = null;
                    foreach (string getParam in app.Context.Request.Url.Query.Replace("?", "").Split('&'))
                    {
                        if (getParam.StartsWith(KEY_GET_PREFIX, CMP_OPTION))
                        {
                            reqKey = getParam.Substring(KEY_GET_PREFIX.Length).Trim();
                            break;
                        }
                    }

                    bool reqDeny = false;
                    if (reqKey == null || reqKey.Length == 0)
                        reqDeny = true;

                    //
                    // Find key permission in file
                    //

                    if (!reqDeny)
                    {
                        reqDeny = true;
                        using(StreamReader reader = new StreamReader(
                            new FileStream(dbFile, FileMode.Open, FileAccess.Read)))
                        {
                            // file syntax:
                            //     key=123&file=/zima.jpg&end=2009.09.20\r\n

                            string DB_KEY = "key=";
                            string DB_FILE = "file=";
                            string DB_END = "end=";
                            string DB_END_FORMAT = "yyyy.MM.dd"; // <- value by default
                            char DB_SPLITTER = '&';

                            if (dateFormat != null)
                                DB_END_FORMAT = dateFormat;

                            while (reader.Peek() >= 0)
                            {
                                string line = reader.ReadLine().Trim();
                                
                                //
                                // Mapping key - case sensitive
                                //
                                
                                if (line.StartsWith(DB_KEY, CMP_OPTION))
                                {
                                    line = line.Substring(DB_KEY.Length);
                                    if (!line.StartsWith(reqKey + DB_SPLITTER))
                                        continue;
                                }
                                else
                                    continue;

                                //
                                // Mapping file
                                //

                                line = line.Substring((reqKey + DB_SPLITTER).Length).Trim();
                                int indexOfSeperator = line.IndexOf(DB_SPLITTER);
                                if (indexOfSeperator < 1)
                                    continue;

                                string rowFile = line.Substring(0, indexOfSeperator);
                                string rowEnd = line.Substring(indexOfSeperator + 1);

                                if (!rowFile.StartsWith(DB_FILE, CMP_OPTION))
                                    continue;

                                rowFile = rowFile.Substring(DB_FILE.Length);
                                rowFile = rowFile.Trim().Replace('\\', '/');

                                if (!app.Context.Request.Url.AbsolutePath.Equals(rowFile, CMP_OPTION))
                                    continue;

                                //
                                // Mapping end date
                                //

                                if (!rowEnd.StartsWith(DB_END, CMP_OPTION))
                                    continue;

                                DateTime? endDate = null;
                                try {
                                    endDate = DateTime.ParseExact(
                                        rowEnd.Substring(DB_END.Length), DB_END_FORMAT, null);
                                }
                                catch { 
                                    /* :-( */ 
                                }

                                DateTime nowDate = DateTime.ParseExact(
                                    DateTime.Now.ToString(DB_END_FORMAT), DB_END_FORMAT, null);
                                if (endDate == null || nowDate > endDate)
                                    continue;

                                //
                                // All condition is Ok
                                //

                                reqDeny = false;
                                break;
                            }
                        }
                    }

                    //
                    // Deny if deny required
                    //
                    
                    if (reqDeny)
                    {
                        app.Context.Response.StatusDescription = "Goodbye, World!";
                        app.Context.Response.StatusCode = 403;
                        app.CompleteRequest();
                    }
                };
        }

        #endregion
    }
}
